﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Xml.Linq;
using Phoenix.Xna.Framework.Properties;
using Phoenix.Xna.Framework.Xml;

namespace Phoenix.Xna.Framework.Serialization.Markup.Objects
{
    /// <summary>
    /// The intermediary markup object for IDictionary types.
    /// </summary>
    internal class DictionaryMarkup : ClassMarkup
    {
        private List<IMarkupObject> _keys = new List<IMarkupObject>();      // The keys in the collection.
        private List<IMarkupObject> _items = new List<IMarkupObject>();     // The items in the collection.
        private Type _t1;
        private Type _t2;

        #region IMarkupElement Members

        /// <summary>
        /// Serialize the element to XML.
        /// </summary>
        /// <returns>The element markup serialzed as an XElement.</returns>
        public override XElement Serialize()
        {
            XElement element = base.Serialize();

            for (int i = 0; i < _keys.Count; i++)
            {
                XElement keyValuePair = new XElement(AttributeIDs.KeyValuePairName);
                keyValuePair.SetAttribute(AttributeIDs.IsKeyValuePairAttribute, true);
                XElement key = _keys[i].Serialize();
                key.SetAttribute(AttributeIDs.IsDictionaryKeyAttribute, true);
                keyValuePair.Add(key);
                XElement item = _items[i].Serialize();
                item.SetAttribute(AttributeIDs.IsCollectionItemAttribute, true);
                keyValuePair.Add(item);
                element.Add(keyValuePair);
            }

            return element;
        }

        /// <summary>
        /// Deserialzie the element from an XElement.
        /// </summary>
        /// <param name="xml">The XElement to deserialize this element from.</param>
        public override void Deserialize(XElement xml)
        {
            base.Deserialize(xml);
            XElement element = xml;

            foreach (XElement node in element.Nodes())
            {
                if (node.ReadAttribute<bool>(AttributeIDs.IsKeyValuePairAttribute))
                {
                    foreach (XElement child in node.Nodes())
                    {
                        if (child.ReadAttribute<bool>(AttributeIDs.IsDictionaryKeyAttribute))
                        {
                            IMarkupObject keyMarkup = Serializer.MarkupSelector.GetMarkupObjectFor(child);
                            _keys.Add(keyMarkup);
                            keyMarkup.Deserialize(child);
                        }
                        else if (child.ReadAttribute<bool>(AttributeIDs.IsCollectionItemAttribute))
                        {
                            IMarkupObject itemMarkup = Serializer.MarkupSelector.GetMarkupObjectFor(child);
                            _items.Add(itemMarkup);
                            itemMarkup.Deserialize(child);
                        }
                    }
                }                
            }
        }

        #endregion

        #region IMarkupObject Members

        /// <summary>
        /// Convert this MarkupObject in to an object model and sets the Instance.
        /// </summary>
        public override void ConstructInstance()
        {
            base.ConstructInstance();
            IDictionary dictionary = (IDictionary)Instance;

            for (int i = 0; i < _keys.Count; i++)
            {
                if (_keys[i].Instance == null)
                {
                    _keys[i].ConstructInstance();
                }

                if (_items[i].Instance == null)
                {
                    _items[i].ConstructInstance();
                }

                dictionary.Add(_keys[i].Instance, _items[i].Instance);
            }            
        }

        /// <summary>
        /// Extract the markup information from the specified object.
        /// </summary>
        /// <param name="instance">The instance to extract markup information for.</param>
        public override void ExtractMarkup(object instance)
        {
            base.ExtractMarkup(instance);

            if (!typeof(IDictionary).IsAssignableFrom(instance.GetType()))
            {
                throw new InvalidCastException(string.Format(CultureInfo.InvariantCulture, Resources.InvalidCastException, instance.GetType().FullName, typeof(IList).Name));
            }

            foreach (object key in (instance as IDictionary).Keys)
            {
                IMarkupObject keyMarkup = Serializer.MarkupSelector.GetMarkupObjectFor(key);
                _keys.Add(keyMarkup);
            }

            foreach (object value in (instance as IDictionary).Values)
            {
                IMarkupObject itemMarkup = Serializer.MarkupSelector.GetMarkupObjectFor(value);
                _items.Add(itemMarkup);
            }
        }

        #endregion
    }
}
